package com.shanzmoo.core.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.shanzmoo.core.domain.product.ProductSkuInfoVo;
import com.shanzmoo.core.util.RedisCoreKeyUtils;
import com.shanzmoo.db.picture.entity.ProductInfoPictureEntity;
import com.shanzmoo.db.picture.entity.ProductPictureEntity;
import com.shanzmoo.db.picture.service.IProductInfoPictureService;
import com.shanzmoo.db.picture.service.IProductPictureService;
import com.shanzmoo.db.product.entity.ProductSkuEntity;
import com.shanzmoo.db.product.entity.ProductSpuEntity;
import com.shanzmoo.db.product.entity.dto.ProductCategoryDTO;
import com.shanzmoo.db.product.entity.dto.ProductSpuActualSaleDTO;
import com.shanzmoo.db.product.entity.dto.ProductSpuDto;
import com.shanzmoo.db.product.service.IProductCategoryService;
import com.shanzmoo.db.product.service.IProductSkuService;
import com.shanzmoo.db.product.service.IProductSpuService;
import com.shanzmoo.core.service.IProductBaseRedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 商品 redis 操作类
 *
 * @author： Gzhao 2020/12/27
 */
@Slf4j
@Service
public class ProductBaseRedisServiceImpl implements IProductBaseRedisService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private IProductSpuService productSpuService;
    @Autowired
    private IProductPictureService productPictureService;
    @Autowired
    private IProductInfoPictureService productInfoPictureService;
    @Autowired
    private IProductSkuService productSkuService;
    @Autowired
    private IProductCategoryService productCategoryService;

    @Override
    public List<ProductSpuDto> obtainAllSpu() {
        return obtainAllSpu(null);
    }

    @Override
    public List<ProductSpuDto> obtainAllSpu(List<String> spuIds) {
        String allSpuKey = RedisCoreKeyUtils.installAllSpuKey();

        if (!stringRedisTemplate.hasKey(allSpuKey)) {
            log.info("缓存中未获取到所有商品数据，开始从数据库中查询");

            productSpuService.getAllSpuDto().forEach(e -> {
                stringRedisTemplate.opsForHash().put(allSpuKey, String.valueOf(e.getId()), JSON.toJSONString(e));
            });
        }

        List<Object> objectList;
        if (CollectionUtil.isNotEmpty(spuIds)) {
            objectList = stringRedisTemplate.opsForHash().multiGet(allSpuKey,
                    spuIds.stream().map(e -> {
                        return (Object) e;
                    }).collect(Collectors.toList()));
        } else {
            objectList = stringRedisTemplate.opsForHash().values(allSpuKey);
        }

        return objectList.stream()
                .map(e -> JSON.parseObject(e.toString(), ProductSpuDto.class))
                .collect(Collectors.toList());
    }


    @Override
    public void updateAllRecommendSpu() {
        String recommendSpuKey = RedisCoreKeyUtils.installRecommendSpuKey();

        stringRedisTemplate.delete(recommendSpuKey);

        List<ProductSpuEntity> list = productSpuService.list(
                new QueryWrapper<ProductSpuEntity>()
                        .select("id")
                        .eq("deleted", false)
                        .eq("shelves", true)
                        .eq("status", true)
                        .eq("recommend", true)
                        .in("channel", 0, 2)
        );

        List<String> ids = list.stream().map(e -> {
            return String.valueOf(e.getId());
        }).collect(Collectors.toList());

        stringRedisTemplate.opsForList().leftPushAll(recommendSpuKey, ids);
    }

    @Override
    public List<String> obtainTopPictureList(String spuId) {
        String spuPictureKey = RedisCoreKeyUtils.installSpuPictureKey();

        if (!stringRedisTemplate.hasKey(spuPictureKey)) {

            List<String> spuIds = obtainAllSpu(null).stream().map(ProductSpuDto::getId).collect(Collectors.toList());

            List<ProductPictureEntity> list = productPictureService.list(
                    new QueryWrapper<ProductPictureEntity>()
                            .eq("deleted", false)
                            .in("product_id", spuIds)
            );
            Map<Integer, List<ProductPictureEntity>> map = list.stream()
                    .collect(Collectors.groupingBy(ProductPictureEntity::getProductId));

            for (Integer spuIdTemp : map.keySet()) {
                List<String> urlList = map.get(spuIdTemp).stream()
                        .map(ProductPictureEntity::getPictureUrl)
                        .collect(Collectors.toList());

                stringRedisTemplate.opsForHash().put(spuPictureKey, String.valueOf(spuIdTemp), JSON.toJSONString(urlList));
            }
        }

        String redisValue = (String) stringRedisTemplate.opsForHash().get(spuPictureKey, String.valueOf(spuId));

        if (StrUtil.isNotEmpty(redisValue)) {
            return JSON.parseArray(redisValue, String.class);
        }
        return Collections.emptyList();
    }

    @Override
    public List<String> obtainInfoPictureList(String spuId) {
        String spuInfoPictureKey = RedisCoreKeyUtils.installSpuInfoPictureKey();

        if (!stringRedisTemplate.hasKey(spuInfoPictureKey)) {

            List<String> spuIds = obtainAllSpu(null).stream()
                    .map(ProductSpuDto::getId).collect(Collectors.toList());


            List<ProductInfoPictureEntity> list = productInfoPictureService.list(
                    new QueryWrapper<ProductInfoPictureEntity>()
                            .eq("deleted", false)
                            .in("product_id", spuIds)
            );

            Map<Integer, List<ProductInfoPictureEntity>> map = list.stream()
                    .collect(Collectors.groupingBy(ProductInfoPictureEntity::getProductId));

            for (Integer spuIdTemp : map.keySet()) {
                List<String> urlList = map.get(spuIdTemp).stream()
                        .map(ProductInfoPictureEntity::getPictureUrl)
                        .collect(Collectors.toList());

                stringRedisTemplate.opsForHash().put(spuInfoPictureKey, String.valueOf(spuIdTemp), JSON.toJSONString(urlList));
            }
        }

        String redisValue = (String) stringRedisTemplate.opsForHash().get(spuInfoPictureKey, String.valueOf(spuId));

        if (StrUtil.isNotEmpty(redisValue)) {
            return JSON.parseArray(redisValue, String.class);
        }
        return Collections.emptyList();
    }

    @Override
    public List<ProductSkuInfoVo> obtainSkuList(String spuId) {
        String skuInfoKey = RedisCoreKeyUtils.installSkuInfoKey(spuId);

        if (!stringRedisTemplate.hasKey(skuInfoKey)) {

            List<ProductSkuEntity> list = productSkuService.list(
                    new QueryWrapper<ProductSkuEntity>()
                            .eq("deleted", false)
                            .eq("parent_id", spuId)
            );

            if (CollectionUtil.isNotEmpty(list)) {

                List<String> skuList = list.stream()
                        .map(e -> {
                            return JSON.toJSONString(ProductSkuInfoVo.create(e));
                        })
                        .collect(Collectors.toList());

                stringRedisTemplate.opsForList().leftPushAll(skuInfoKey, skuList);
            }
        }

        List<String> redisValue = stringRedisTemplate.opsForList().range(skuInfoKey, 0, -1);

        return redisValue.stream()
                .map(e -> {
                    return JSON.parseObject(e, ProductSkuInfoVo.class);
                }).collect(Collectors.toList());
    }

    @Override
    public List<ProductCategoryDTO> obtainAllCategory() {
        String key = RedisCoreKeyUtils.installAllCategoryKey();

        List<ProductCategoryDTO> allCategoryList;

        Long size = stringRedisTemplate.opsForList().size(key);
        if (size == 0) {
            log.info("缓存中未获取到所有商品分类数据，开始从数据库中查询");

            allCategoryList = productCategoryService.getAllCategory();

            List<String> reListStr = allCategoryList.stream()
                    .map(e -> {
                        return JSON.toJSONString(e);
                    }).collect(Collectors.toList());
            stringRedisTemplate.opsForList().leftPushAll(key, reListStr);
        } else {

            List<String> reListStr = stringRedisTemplate.opsForList().range(key, 0, -1);

            allCategoryList = reListStr.stream().map(e -> {
                return JSON.parseObject(e, ProductCategoryDTO.class);
            }).collect(Collectors.toList());
        }
        return allCategoryList;
    }

    @Override
    public Map<String, Integer> obatianSpuSalesMap(List<String> spuIds) {
        String spuSalesKey = RedisCoreKeyUtils.installSpuSalesKey();

        if (!stringRedisTemplate.hasKey(spuSalesKey)) {
            log.info("缓存中未获取到所有商品实际销量数据，开始从数据库中查询");
            List<String> allSpuIds = obtainAllSpu().stream().map(ProductSpuDto::getId).collect(Collectors.toList());

            List<ProductSpuActualSaleDTO> spuActualSales = productSpuService.getSpuActualSales(allSpuIds);

            Map<String, String> map = spuActualSales.stream().collect(Collectors.toMap(ProductSpuActualSaleDTO::getId, ProductSpuActualSaleDTO::getActualSales));
            stringRedisTemplate.opsForHash().putAll(spuSalesKey, map);
        }
        Map<Object, Object> allData = stringRedisTemplate.opsForHash().entries(spuSalesKey);

        Map<String, Integer> result = new HashMap<>();
        for (String spuId : spuIds){
            result.put(spuId, NumberUtil.parseInt((String) allData.get(spuId)));
        }
        return result;
    }

    @Override
    public Map<String, String> obtainSearchSpuList() {
        List<ProductSpuDto> allSpuList = obtainAllSpu();

        Map<String, String> result = new HashMap<>();
        allSpuList.forEach(e->{
            result.put(String.valueOf(e.getId()), e.getName());
        });
        return result;
    }
}
